package xj.graph2d.app.prop;

import static xj.app.BaseApplicationFrame.getResourceString;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Iterator;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JToolBar;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.text.EditorKit;
import javax.swing.border.BevelBorder;

import xj.graph2d.app.resources.Style;
import xj.model.uml.BasicModelElement;
import xj.model.uml.Constraint;
import xj.model.uml.NamedElement;

import jsyntaxpane.DefaultSyntaxKit;

public class ConstraintField 
  extends ModelPropertyField 
  implements ItemListener {

  public ConstraintField() {
  }

  public ConstraintField(Frame owner) { 
    super("constraint", getResourceString("Constraints.text"));
    JButton editButton = makeEditButton(getResourceString("Constraints.edit.text"));
    editButton.setToolTipText(getResourceString("Constraints.edit.tip"));

    constraintDialog = new ConstraintDialog(owner, getResourceString("Constraints.dialog.title"),
					    this);
    editButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	updateEditorValue();
	constraintDialog.showDialog();
      }
    });
    setEditor(editButton);
  }

  public String[] getDefaultLanguageNames() { 
    return defaultLanguageNames;
  }

  public void setDefaultLanguageNames(String[] names) {
    defaultLanguageNames = names; 
  }

  public String[] getDefaultConstraintNames() { 
    return defaultConstraintNames;
  }

  public void setDefaultConstraintNames(String[] names) { 
    defaultConstraintNames = names;
  }

  public void setDialogSize(int w, int h) {
    if (constraintDialog != null) {
      constraintDialog.setSize(w, h);
    }
  }

  @Override
  protected void updateTips() {
    if (editor != null) {
      String prefix = "<html><body bgcolor='white'><b>Constraints</b><br>";
      String suffix = "</body></html>";
      StringBuilder sb = new StringBuilder();
      if (model instanceof BasicModelElement) {
	BasicModelElement m = (BasicModelElement) model;
	Set<String> names = m.getConstraintNames();
	if (names != null && names.size() > 0) {
	  sb.append("<ol>");
	  Iterator<String> iter = names.iterator();
	  while (iter.hasNext()) {
	    String name = iter.next();
	    Constraint c = m.getConstraint(name);
	    if (c != null) {
	      String lang = c.getLang();
	      String body = c.getBody();
	      sb.append("<li><font color='red'><b>");
	      sb.append((name != null && name.length() > 0) ? name : "unnamed");
	      sb.append("</b></font>&nbsp;<i>").append(lang).append("</i><br>");
	      sb.append(body.replaceAll("\n", "<br>"));
	      sb.append("</li>");
	    }
	  }
	  sb.append("</ol>");
	}
      }

      String text = null;
      if (sb.length() > 0) { 
	text = sb.toString();
      } else { 
	text = "&nbsp;&nbsp;<i>none</i>";
      }
      editor.setToolTipText(prefix + text + suffix);
    }
  }

  @Override
  public void updateEditorValue() {
    if (model instanceof BasicModelElement) {
      BasicModelElement m = (BasicModelElement) model;
      Set<String> names = m.getConstraintNames();
      if (defaultConstraintNames != null) { 
	for (int i = 0; i < defaultConstraintNames.length; i++) {
	  names.add(defaultConstraintNames[i]); 
	} 
      }
      constraintPanel.setNames(names);
      String cname = constraintPanel.getConstraintName();
      constraintPanel.readConstraint(m, cname);
    }

    if (constraintDialog != null) {
      String name = null;
      String element = null;
      if (model instanceof NamedElement) {
	name = ((NamedElement) model).getName();
      }
      if (name == null || name.length() == 0) {
	name = "<unnamed>";
      }
      element = "[" + model.getElementName() + "]";

      constraintDialog.setTopTitle("Contraints of", name, element);
    }
  }

  @Override
  public void updateModelValue() {
    constraintPanel.writeConstraint(constraintPanel.getConstraintName());
    constraintPanel.deactivate();

    updateTips();
  }

  public void itemStateChanged(ItemEvent e) {
    if (e != null) {
      String item = (String) e.getItem();
      if (e.getStateChange() == ItemEvent.DESELECTED) {
	// System.out.println("ConstraintField.itemStateChanged(): DESELECT " + item);
	constraintPanel.writeConstraint(item);
      } else {
	// SELECTED
	// System.out.println("ConstraintField.itemStateChanged(): SELECT " + item);
	constraintPanel.readConstraint(item);
      }
    }
  }

  protected ConstraintDialog constraintDialog;

  protected ConstraintPanel constraintPanel;

  protected String[] defaultLanguageNames = null;
  protected String[] defaultConstraintNames = null;

}

class ConstraintDialog extends JDialog implements ActionListener {

  public ConstraintDialog(Frame owner, String title, ConstraintField field) {
    super(owner, title, true);
    setAlwaysOnTop(true);

    this.field = field;

    JPanel topPanel = new JPanel();
    topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 5, 10));
    topPanel.setLayout(new BorderLayout());
    add(topPanel);

    titleLabel = new TitleComponent();
    titleLabel.setNameStyle(Font.ITALIC);
    titleLabel.setNameColor(new Color(0, 0, 128));
    titleLabel.setElementColor(new Color(0, 128, 0));
    topPanel.add(titleLabel, BorderLayout.NORTH);

    constraintPanel = new ConstraintPanel(field);
    topPanel.add(constraintPanel, BorderLayout.CENTER);

    JPanel buttonPanel = new JPanel();
    buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
    buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
    cancelButton = new JButton("Cancel");
    cancelButton.addActionListener(this);
    buttonPanel.add(cancelButton);
    okButton = new JButton("Ok");
    okButton.addActionListener(this);
    buttonPanel.add(okButton);
    topPanel.add(buttonPanel, BorderLayout.SOUTH);

    setSize(800, 500);
  }

  public void showDialog() {
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension dialogSize = getSize();
    setLocation(screenSize.width / 2 - dialogSize.width / 2, 
		screenSize.height / 2 - dialogSize.height / 2);
    setVisible(true);
  }

  public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    if (source == okButton) {
      if (field != null) {
	field.updateModelValue();
	field.updateShape();
      }
      setVisible(false);
    } else {
      // cancel
      setVisible(false);
      constraintPanel.deactivate();
    }
  }

  public void setTopTitle(String prefix, String name, String element) {
    if (constraintPanel != null &&
	titleLabel != null) { 
      constraintPanel.setBorder(BorderFactory.createEtchedBorder());
      titleLabel.setTitle(prefix, name, element);
    }
  }

  protected ConstraintField field;

  protected ConstraintPanel constraintPanel;

  protected JButton okButton, cancelButton;

  protected TitleComponent titleLabel;

}

class ConstraintPanel extends JPanel {

  ConstraintPanel(ConstraintField field) {
    this(field, true); 
  }

  ConstraintPanel(ConstraintField field, boolean hasToolbar) {
    if (field != null) {
      field.constraintPanel = this;
    }

    DefaultSyntaxKit.initKit();
    body = new JEditorPane();
    JScrollPane scrollPane = new JScrollPane(body);
    body.setFont(Style.font);
    body.setContentType("text/plain");
    body.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));

    if (hasToolbar) { 
      toolbar = new JToolBar();
      toolbar.setRollover(true);
      toolbar.setFocusable(false);
      toolbar.setFloatable(false);

      EditorKit kit = body.getEditorKit();
      if (kit instanceof DefaultSyntaxKit) {
	DefaultSyntaxKit defaultSyntaxKit = (DefaultSyntaxKit) kit;
	defaultSyntaxKit.addToolBarActions(body, toolbar);
      }
      toolbar.validate();
    }

    name = new JComboBox();
    name.addItem("");
    String[] consNames = field.getDefaultConstraintNames();
    if (consNames != null && consNames.length > 0) { 
      for (int i = 0; i < consNames.length; i++) {
	name.addItem(consNames[i]); 
      } 
    }
    name.setEditable(true);
    name.setBackground(Color.white);
    name.setFont(Style.editorFont);
    name.addItemListener(field);

    lang = new JComboBox();
    lang.addItem("");
    String[] langNames = field.getDefaultLanguageNames();
    if (langNames != null && langNames.length > 0) { 
      for (int i = 0; i < langNames.length; i++) { 
	lang.addItem(langNames[i]);
      } 
    } else { 
      lang.addItem("informal");
      lang.addItem("OCL");
      //lang.addItem("Java");
    }
    lang.setEditable(true);
    lang.setBackground(Color.white);
    lang.setFont(Style.editorFont);

    lang.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent e) {
	if (e.getStateChange() == ItemEvent.SELECTED) {
	  String text = body.getText();
	  String langType = lang.getSelectedItem().toString();
	  if (langType != null) { 
	    langType = langType.toLowerCase(); 
	  }
	  String contentType = "text/plain";
	  if ("java".equals(langType)) { 
	    contentType = "text/java";
	  } else if ("html".equals(langType)) { 
	    contentType = "text/xhtml";
	  }
	  body.setContentType(contentType); 
	  body.setText(text);
	  if (toolbar != null) { 
	    toolbar.removeAll();
	    EditorKit kit = body.getEditorKit();
	    if (kit instanceof DefaultSyntaxKit) {
	      DefaultSyntaxKit defaultSyntaxKit = (DefaultSyntaxKit) kit;
	      defaultSyntaxKit.addToolBarActions(body, toolbar);
	    }
	    toolbar.validate();
	  }
	  body.requestFocusInWindow();

	  //System.out.println("ConstraintPanel.itemStateChanged(): contentType " + body.getContentType());
	}
      }
    });

    setLayout(new BorderLayout());

    topPanel = new JPanel();
    topPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    topPanel.add(new JLabel("Name"));
    topPanel.add(name);
    topPanel.add(new JSeparator());
    topPanel.add(new JLabel("Language"));
    topPanel.add(lang);
    if (toolbar != null) { 
      topPanel.add(toolbar);
    }

    add(topPanel, BorderLayout.NORTH);
    add(scrollPane, BorderLayout.CENTER);
  }

  void setNames(Set<String> names) {
    // System.out.println("ConstraintPanel.setNames() name=" + names);

    name.removeAllItems();
    if (names != null) {
      name.addItem("");
      Iterator<String> iter = names.iterator();
      while (iter.hasNext()) {
	String s = iter.next();
	if (s != null && s.length() > 0) { 
	  name.addItem(s);
	}
      }
      if (names.size() > 0) {
	name.setSelectedIndex(1);
      }
    }
  }

  String getConstraintName() {
    if (name != null) {
      return (String) name.getSelectedItem();
    }
    return null;
  }

  boolean setConstraintName(String s) {
    boolean found = false;
    if (name != null && s != null) {
      int count = name.getItemCount();
      for (int i = 0; i < count; i++) {
	Object item = name.getItemAt(i);
	if (s.equals(item)) {
	  found = true;
	  break;
	}
      }
      if (!found) {
	name.addItem(s);
      }
      name.setSelectedItem(s);
    }
    return found;
  }

  String getConstraintLang() {
    if (lang != null) {
      return (String) lang.getSelectedItem();
    }
    return null;
  }

  void setConstraintLang(String s) {
    if (lang != null) {
      if (s != null) {
	lang.setSelectedItem(s);
      }
    }
  }

  String getConstraintBody() {
    if (body != null) {
      return body.getText();
    }
    return null;
  }

  void setConstraintBody(String s) {
    if (body != null) {
      body.setText(s);
    }
  }

  void deactivate() {
    setConstraintName(null);
    setConstraintLang(null);
    setConstraintBody(null);
    active = false;
  }

  void writeConstraint(String name) {
    if (active && model != null) {
      model.removeConstraint(name);
      String body = getConstraintBody();
      if (body != null) {
	body = body.trim();
	if (body.length() > 0) {
	  model.setConstraint(name, getConstraintLang(), body);
	}
      }
    }
  }

  void readConstraint(BasicModelElement model, String name) {
    this.model = model;
    readConstraint(name);
    active = true;
  }

  void readConstraint(String name) {
    if (model != null) {
      Constraint c = model.getConstraint(name);
      if (c != null) {
	setConstraintName(name);
	setConstraintLang(c.getLang());
	setConstraintBody(c.getBody());
      } else {
	setConstraintName(name);
	setConstraintLang(null);
	setConstraintBody(null);
      }
    }
  }

  protected BasicModelElement model;

  protected boolean active = false;

  protected JPanel topPanel; 

  protected JEditorPane body;

  protected JComboBox lang;

  protected JComboBox name;

  protected JToolBar toolbar; 

}
